#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//1.命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;
//2.环境变量表
#define MAX_ENV 100
char *g_env[MAX_ENV];
int g_envs = 0;
//退出码
int lastcode = 0;
//关于重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
int redir = NONE_REDIR;
std::string filename; 
const char *GetUserName()
{
  char *name = getenv("USER");
  return name == NULL ? "None" : name;
}
const char *GetHostName()
{
  char *hostname = getenv("HOSTNAME");
  return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()
{
  char *pwd = getenv("PWD");
  return pwd == NULL ? "None" : pwd;
}
std::string DirName(const char *pwd)
{
#define SLASH "/" 
  std::string dir = pwd;
  if(dir == SLASH) return SLASH;
  auto pos = dir.rfind(SLASH);
  if(pos == std::string::npos) return "BUG?";
  return dir.substr(pos + 1);
}
void MakeCommandLine(char cmd_prompt[],int size)
{
  snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
  //snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt,sizeof(prompt));
  printf("%s",prompt);
  fflush(stdout);
}
bool GetCommandLine(char *out,int size)
{
  char *c = fgets(out,size,stdin);
  if(c  == NULL) return false;
  out[strlen(out) - 1] = 0;
  if(strlen(out) == 0) return false;
  return  true;
}
bool CommandPrase(char *commandline)
{
#define SEP " "
  g_argc = 0;
  g_argv[g_argc++] = strtok(commandline,SEP);
  while(bool(g_argv[g_argc++] = strtok(nullptr,SEP)));
  g_argc--;
  return true;
}
int Execute()
{
  pid_t id = fork();
  if(id == 0)
  {
    int fd = -1;
    if(redir == INPUT_REDIR)
    {
      fd = open(filename.c_str(),O_RDONLY);
      if(fd < 0) exit(1);
      dup2(fd,0);
      close(fd);
    }
    else if(redir == OUTPUT_REDIR)
    {
      fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC,0666);
      if(fd < 0) exit(2);
      dup2(fd,1);
      close(fd);
    }
    else if(redir == APPEND_REDIR)
    {
      fd = open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND,0666);
      if(fd < 0) exit(2);
      dup2(fd,1);
      close(fd);
    }
    else{}
    execvp(g_argv[0],g_argv);
    exit(1);
  }
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    lastcode = WEXITSTATUS(status);
  }
  return 0;
}
void PrintArgv()
{
  for(int i = 0;g_argv[i];i++)
  {
    printf("argv[%d]->%s\n",i,g_argv[i]);
  }
  printf("argc:%d\n",g_argc);
}
const char *GetHome()
{
  const char *home = getenv("HOME");
  return home == NULL ? "" : home;
}
bool Cd()
{
  if(g_argc == 1)
  {
    std::string home = GetHome();
    if(home.empty()) return true;
    chdir(home.c_str());
  }
  else 
  {
    std::string where = g_argv[1];
    if(where == "~")
    {
      //家目录
    }
    else if(where == "-")
    {
      //上个目录
    }
    else
    {
      chdir(where.c_str());
    }
    return true;
  }
  return false;
}
void Echo()
{
  if(g_argc == 2)
  {
    std::string opt = g_argv[1]; 
    if(opt == "$?")
    {
      std::cout << lastcode << std::endl;
      lastcode = 0;
    }
    else if(opt[0] == '$')
    {
      std::string env_name = opt.substr(1);
      const char *env_value = getenv(env_name.c_str());
      std::cout << env_value << std::endl; 
    }
    else 
    {
      std::cout << opt << std::endl;
    }
  }
}
bool CheckAndExecBuiltin()
{
  std::string cmd = g_argv[0];
  if(cmd == "cd")
  {
    Cd();
    return true;
  }
  else if(cmd == "echo")
  {
    Echo();
    return true;
  }
  return false;;
}
void InitEnv()
{
  extern char **environ;
  memset(g_env,0,sizeof(g_env));
  g_envs = 0;
  //获取环境变量,申请空间
  for(int i = 0;environ[i];i++)
  {
    g_env[i] = (char*)malloc(strlen(environ[i] + 1));
    strcpy(g_env[i],environ[i]);
    g_envs++;
  }
  g_env[g_envs++] = (char*)"Myvalur=666666";
  g_env[g_envs] = NULL;
  //导成环境变量
  for(int i = 0;g_env[i];i++)
  {
    putenv(g_env[i]);
  }
}
void TrimSpace(const char *cmd,int& end)
{
  while(isspace(cmd[end]))
  {
    end++;
  }
}
void RedirCheck(char cmd[])
{
  redir = NONE_REDIR;
  filename.clear();
  int start = 0;
  int end = strlen(cmd) - 1;
  while(end > start)
  {
    if(cmd[end] == '<')
    {
      cmd[end++] = 0;
      TrimSpace(cmd,end);
      redir = INPUT_REDIR;
      filename = cmd + end;
      break;
    }
    else if(cmd[end] == '>')
    {
      if(cmd[end - 1] == '>')
      {
        cmd[end - 1] = 0;
        redir = APPEND_REDIR;
      }
      else 
      {
        redir = OUTPUT_REDIR;
      }
        cmd[end++] = 0;
        TrimSpace(cmd,end);
        filename = cmd + end;
        break;
    }
    else
    {
      end--; 
    }
  }
}
int main()
{
  while(true)
  {
    //初始化环境变量
    InitEnv();
    //输出命令提示符
    PrintCommandPrompt();
    //获取用户输入的命令
    char commandline[COMMAND_SIZE];
    if(!GetCommandLine(commandline,sizeof(commandline)))
      continue;
    //重定向分析
    RedirCheck(commandline);
    //命令行分析
    if(!CommandPrase(commandline))
      continue;
    //检测并处理內键命令
    if(CheckAndExecBuiltin())
      continue;
    //执行命令
    Execute();
  }
  return 0;
}
